Python中的装饰器、迭代器、生成器、推导式、匿名函数和高阶函数 – python程序 您所在的位置:网站首页 python 打印迭代器 Python中的装饰器、迭代器、生成器、推导式、匿名函数和高阶函数 – python程序

Python中的装饰器、迭代器、生成器、推导式、匿名函数和高阶函数 – python程序

2023-03-29 07:46| 来源: 网络整理| 查看: 265

文章目录 装饰器 迭代器 生成器 推导式 匿名函数 高阶函数 装饰器

闭包

介绍装饰器前先了解一下闭包,在Python中,一切皆对象(Object),函数(Function)也不例外,也是一个普通的对象,函数可以作为一个参数传给其它函数,函数的返回值也可以是一个函数,而这种相关参数和变量都保存在返回的函数中的形式,就称为闭包(Closure),如下示例

def my_closure(): # 使用函数嵌套,先定义一个外部函数 num0 = 1 # 在外部函数中定义一个常量 def my_out(num1,num2): # 再定义一个内部函数,并动态传参,此参数外部无法调用 res = num0 + num1 # 再定义内部函数变量,并且使用外部函数中的变量 res1 = num2 - num0 print(res,res1) # 输出结果 return my_out # 返回内部函数,这种方式就是闭包函数 output = my_closure() # 获取闭包对象,赋值给output output(2,5) # 执行闭包,传参为2和5,输出结果为3和4 1 2 3 4 5 6 7 8 9 10

闭包的作用是使参数和变量一直保存在内存中,不会被垃圾回收机制收回

此处简单介绍一下函数中global和nonlocal的区别,了解二者的作用域

nonlocal是设置为局部变量,必须在嵌套函数中使用,必须先在外部函数定义变量,再在内部函数使用nonlocal声明,表示二者是同一个变量,所以只会修改上一层函数中定义的变量,nonlocal在闭包函数中经常出现

global是设置为全局变量,可以用在最上层函数和嵌套函数中,即使之前未定义变量,修饰后也可以直接使用,在嵌套函数中,不能修改上一层函数中的变量,若只有一个函数就会修改外部变量的值

如下示例

# nonlocal n = 0 # 定义一个全局变量n,并设置初始值为0 def func(): # 定义一个外部函数func() n = 1 # 在外部函数中再定一个n=1的局部变量,对于func1()函数而言,n=1就是外部变量,不能直接使用 def func1(): # 再定一个内部函数func1() nonlocal n # 设置n为局部变量,同样只会改变当前作用域func1函数中n的值,但,是将其赋值给n=1,而不是n=0 n = 2 # 定义一个局部变量,因为设置了n为局部变量,所以程序运行时就可以使用n=1,将1改为2,然后开始执行函数 for i in range(1,101): n += i # 计算n = n+i func1() # 执行内部函数func1() print("局部变量输出:",n) # 因为在func1()函数中的变量已通过nonlocal设为局部变量,参与了运算,所有输出为n = 2+5050 func() # 再调用func() print("全局变量输出:",n) # 因为n=0未参与运算,所以直接输出为0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # golbal n = 0 # 定义一个全局变量n,并设置初始值为0 def func(): # 定义一个外部函数func() n = 1 # 在外部函数中再定一个局部变量n,并设置初始值为1 def func1(): # 再定一个内部函数func1() global n # 设置n为全局变量,它只会改变当前作用域func1函数中n的值,并赋值给n=0,不会改变n=1 n = 2 # 定义一个局部变量,因为设置了n为全局变量,所以程序运行时就是将n=0设置为n=2,然后开始执行函数 for i in range(1,101): n += i # 计算n = n+i func1() # 执行内部函数func1() print("func1()输出:",n) # 因为n=1未参与运算,所以直接打印结果为1 func() # 再调用func() print("func()输出:",n) # 因为n=2替换了n=0,然后参与了func1()函数中的运算,最终计算结果n = 2+5050 1 2 3 4 5 6 7 8 9 10 11 12 13 14

请添加图片描述

简单装饰器

装饰器(Decorator)实质上也可理解为一个闭包函数,当只有一个函数类型的参数时,就可称为装饰器,否则就称为闭包函数,如下示例

import time # 导入time库 def run_time(func): # 先定义一个外部函数,func是需要传入装饰器的函数对象 def wrapper(): # 再定义一个内部函数 t1 = time.time() # 获取被调用函数运行前的时间 func() # 被调用的函数在此处运行,如下面定义的even_nums()函数 t2 = time.time() # 获取被调用函数运行后的时间 print("运行时间:{:.2}s".format(t2 - t1)) # 两时间相减得到实际运行时长,保留3位小数 return wrapper # 返回内部函数执行结果 @run_time # 使用@符号调用函数时被称为装饰器,目的是不改变even_nums()函数的前提下,给他增加计时功能 def even_nums(): # 定义一个普通函数 for i in range(1, 20000):# 使用for循环输出20000以内的所有偶数 if i % 2 == 0: print(i) even_nums() # 执行even_nums()函数,同时会输出此函数执行时长 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

带参数的装饰器

import time # 导入time库 def run_time(func): # 先定义一个外部函数,func是需要传入装饰器的函数对象 def wrapper(*args,**kwargs): # 再定义一个内部函数,允许传入多个位置参数和关键字参数 t1 = time.time() # 获取被调用函数运行前的时间 res = func(*args,**kwargs) # 被调用的函数在此处运行,允许传入任意参数,将被调函数执行结果赋值给res t2 = time.time() # 获取被调用函数运行后的时间 print("运行时间:{:.2}s".format(t2 - t1)) # 两时间相减得到实际运行时长,保留3位小数 return res # 返回被调函数执行结果 return wrapper # 返回内部函数执行结果 @run_time # 使用装饰器,等价于even_nums=run_time(even_nums) def even_nums(maxnum): # 定义一个普通函数 for i in range(1, maxnum): # 动态传参,使用for循环循环输出偶数 if i % 2 == 0: print(i) @run_time # 使用装饰器 def names(zs,ls,ww): # 输出对应的中文名称 time.sleep(0.2) # 响应太快,为了显示时间,刻意加上固定等待0.2秒 print(f"打印中文名:{zs},{ls},{ww}") # 输出中文名 even_nums(20000) # 执行even_nums()函数,传入位置参数,同时会输出此函数执行时长 names(ls="李思",ww="王武",zs="张珊") # 执行names()函数,传入多个关键字参数,同时会输出此函数执行时长 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

打印日志也可以使用装饰器,示例如下

import logging # 导入logging库 logger = logging.getLogger() # 定义日志并设置名称然后赋值给logger logger.setLevel(logging.DEBUG) # 设置日志为DEBUG级别 # ↓定义日志格式,输出格式为:当前时间 - 日志等级 - 函数名 - 日志信息 format = logging.Formatter("%(asctime)s - %(levelname)s - %(funcName)s - %(message)s") yd = logging.StreamHandler() # 输出日志记录 yd.setFormatter(format) # 输出日志使用定义的format格式 logger.addHandler(yd) # 日志输出到控制台 def logging(func): # 定义一个外部函数 def wrapper(*args,**kwargs): # 定义一个内部函数,运行传入任何参数 res = func(*args,**kwargs) # 被调用的函数在此处运行,允许传入任意参数,将被调函数执行结果赋值给res # ↓调用定义的日志,输出INFO等级的日志,格式为:“当前时间-INFO-wrapper-执行函数名,参数值,响应结果” logger.info(f"执行函数:{func.__name__},参数:{args}{kwargs},响应结果:{res}") return res # 返回被调函数执行结果 return wrapper # 返回内部函数执行结果 @logging # 每个函数都可以调用装饰器 def add_num(x, y): # 定义函数 return x+y # 返回xy相加结果 @logging # 使用装饰器 def power(i, n): return i**n # 返回i的n次方结果 @logging # 使用装饰器 def concat(a,b,c,d): return a+b+c+d # 拼接abcd add_num(3, 5) # 执行函数 power(n=2,i=3) concat("2","a",d="3",c="b") 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

请添加图片描述

带参数的类装饰器

class cldecorator(object): # 定义一个类 def __init__(self, output): # 初始化实例对象,增加out变量,初始值为output self.out = output def __call__(self, func): # 使实例对象可以作为函数调用 def wrapper(*args, **kwargs): # 定义一个函数,允许传入任何参数 res = func(*args, **kwargs) # 被调用的函数在此处运行,允许传入任意参数,将被调函数执行结果赋值给res print(f"{self.out}:{res}") # 打印初始变量和被调用函数执行结果 return res # 返回被调用函数执行结果 return wrapper # 返回wrapper函数执行结果 @cldecorator(output="计算结果") # 使用装饰器,并传入参数 def add_subtract(x, y): # 创建普通函数 return x - y # 返回参数相减结果 add_subtract(6, 4) # 执行add_subtract()函数,响应结果为“计算结果:2” 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

装饰器的作用是为了在不改变现有函数的情况下,给该函数增加额外的功能,同时提示代码的简洁性、易读性及执行效率

迭代器

在for循环背后有两个核心概念,可迭代对象(iterable)和叫迭代器(iterator),先熟悉一下这两个概念

若一个对象只实现了__iter__方法,但未实现__next__方法,那么就是可迭代对象,而非迭代器

若一个对象既实现了__iter__方法,又实现__next__方法,那么既是可迭代对象,也是迭代器

from collections.abc import Iterator, Iterable my_list = [1, 2] print(f"判断my_list是否是可迭代对象:{isinstance(my_list, Iterable)}") print(f"判断my_list是否是迭代器:{isinstance(my_list, Iterator)}") print(f"查看my_list的方法:{dir(my_list)}") 1 2 3 4 5 6

请添加图片描述

from collections.abc import Iterator, Iterable my_list1 = [1, 2].__iter__() # 生成my_list1迭代器对象 print(f"判断my_list1是否是可迭代对象:{isinstance(my_list1, Iterable)}") print(f"判断my_list1是否是迭代器:{isinstance(my_list1, Iterator)}") print(f"查看my_list1的方法:{dir(my_list1)}") print(my_list1.__next__()) # 使用__next__方法逐个取值 print(my_list1.__next__()) print(my_list1.__next__()) # 第三次取值时,因为无值可取,抛出StopIteration异常 1 2 3 4 5 6 7 8 9 10

请添加图片描述

使用for在循环时,循环内部是先执行__iter__方法,获取其迭代对象,然后在内部执行这个迭代器对象的__next__方法,逐个取值,最后无值可取,会break跳出循环,所以在使用for循环获取内部数据时才看不到有异常抛出

my_list1 = [1, 2].__iter__() # 生成my_list1迭代器对象 for i in my_list1: print(i) 1 2 3

此处[1, 2].__iter__()就是迭代器,在for循环中,此位置可能是可迭代对象(iterable)、也可能是迭代器(Iterator)、也可能是生成器(Generator)

生成器

如下示例,通过查看返回结果,先了解一下return和yield的区别

def return_odd(maxnum): # 创建一个函数 for i in range(maxnum): # 获取奇数 if i%2 == 1: return i # 使用return返回 odd = return_odd(5) # 参数为5,执行函数并将结果赋值给odd print(odd) # 打印odd的值 print(dir(odd)) # 打印odd的方法 def yield_odd(maxnum): # 创建一个函数,这个因为使用了yield,实际这个就是生成器函数 for i in range(maxnum): if i%2 == 1: yield i # 使用yield返回 odd = yield_odd(5) # 参数为5,执行函数并将结果赋值给odd,odd就是生成器对象 print(odd) # 打印odd的值 print(dir(odd)) # 打印odd的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

请添加图片描述

从结果中可以看出,return是直接返回表达式的值,并且执行到return函数就结束啦,而yield返回的是一个生成器对象,返回的方法中包含__iter__方法和__next__方法

获取生成器中的数据,转换成列表展示,或者使用for循环获取每个数据

def yield_odd(maxnum): for i in range(maxnum): if i % 2 == 1: yield i odd = yield_odd(5) print(list(odd)) # 转成列表展示 for i in odd: # 此处的odd就是生成器 print(i) 1 2 3 4 5 6 7 8 9

迭代器与生成器的区别,如下示例

# 迭代器 my_list1 = [0, 1].__iter__() for i in my_list1: # 获取迭代器中的数据和对应内存中地址 print(i, id(i)) print("\n") # 生成器 def my_generator(maxnum): for i in range(maxnum): yield i print(list(my_generator(2)))# 结果转换成列表展示 for i in my_generator(2): # 获取生成器中的数据和对应内存中地址 print(i, id(gen)) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

请添加图片描述

通过示例结果可以看出:

迭代器是对可迭代对象中所有的数据进行遍历,并将每个数据保存到内存中 生成器返回的内存id都是一样的,这是因为获取数据时遇到yield会临时暂停处理,并记住当前位置执行状态,数据读取并使用后会立即从内存中释放掉,然后再获取下一个数据,当该生成器恢复时,会从离开位置继续执行,获取新的数据后又放到了原来的内存中,所以两次结果的内存id都是相同的,所以生成器是只将当前数据保存到内存中,每个数据只能读取一次

通过以上了解可知,生成器其实就是一个特殊的迭代器,目的是为了节省内存空间,但使用生成器只能迭代一次,所以工作中如果确定对生成的数据只使用一次时可以考虑使用Python生成器

推导式

推导式又叫做解析式,也有称为生成式、迭代式的,指对列表(list),字典(dict),集合(set)和元祖(tuple)在原有数据上根据条件生成新的数据,可以帮助我们把一个序列或者其它可迭代类型中的元素通过过滤或加工的方式,生成所需的新数据,基本语法为表达式 for 迭代变量 in 可迭代对象 [if 条件表达式],中括号内的表示可选参数

列表推导式

给出一个列表num = [2,6,7],根据条件可以生成新的列表,示例如下

# ↓循环num列表生成名为my_list0的新列表,结果为[2, 6, 7] my_list0 = [i for i in num] # ↓计算num列表中数据的平方,生成名为my_list1的新列表,结果为[4, 36, 49] my_list1 = [i**2 for i in num] # ↓计算num列表中满足if条件的数据,生成名为my_list3的新列表,结果为[16, 17] my_list2 = [i+10 for i in num if i > 3] # ↓计算num列表中数据,生成名为my_list2的嵌套列表,结果为[[3, 4], [7, 36], [8, 49]] my_list3 = [[i+1,i*i] for i in num] # ↓循环嵌套,提取my_list4嵌套列表中的偶数,生成名为my_list5的新列表,结果为[4, 36, 8] my_list4 = [n for i in my_list3 for n in i if n%2==0] # mylist4列表推导式等价于下面的普通写法 num = [] for i in my_list3: for n in i: if n%2==0: num.append(n) print(num) # 输出结果同样为[4, 36, 8] # ↓计算num列表中满足if条件的数据,生成名为my_list5的新列表,结果为['dyd-2', 'dyd-6'] my_list5 = [f"dyd-{i}" for i in num if i i+1:i*2 for i in range(1,4)} # 结果为{2: 2, 3: 4, 4: 6} my_dict2 = {f"param{i}":[i+2,i] for i in range(1,4) if i != 3} # 结果为{'param1': [3, 1], 'param2': [4, 2]} my_dict3 = {f"param{i}":(i+5,i) for i in range(1,4) if i >= 2} # 结果为{'param2': (7, 2), 'param3': (8, 3)} # ↓根据personal_info字典中的元素按照“键=值”格式化,并使用&符号进行连接,最终与https://www.dyd.com?拼接成新的url personal_info = {"name":"yd","id":1,"age":22} # ↓输出结果为https://www.dyd.com?name=yd&id=1&age=22 my_dict4 = "https://www.dyd.com?"+"&".join([f"{k}={v}" for k,v in personal_info.items()]) # mydict4字典推导式等价于下面的普通写法 spl=[] for k,v in personal_info.items(): kv = f"{k}={v}" spl.append(kv) res = "https://www.dyd.com?"+"&".join(spl) print(res) # 输出结果同样为https://www.dyd.com?name=yd&id=1&age=22 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

集合推导式

集合和字典一样都是用花括号表示,只是集合元素不可以是列表,若要与字典区分,可以使用set()表示

my_set0 = {i for i in range(5)} # 结果为{0, 1, 2, 3, 4} my_set1 = {(i+2,i,i-1) for i in range(5)} # 结果为{(6, 4, 3), (5, 3, 2), (3, 1, 0), (2, 0, -1), (4, 2, 1)} my_set2 = {(i,i,i) for i in range(5) if i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有